package tinf.pokretac;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;

import tinf.entropijsko.LZ77;
import tinf.entropijsko.UredjenaTrojka;
import tinf.entropijsko.binarno.LZ77Binarno;
import tinf.hamming.HammingCode743;
import tinf.ispis.IspisUDatoteku;
import tinf.izvoriste.Izvoriste;
import tinf.kanal.Kanal;

/**
 * Klasa pokreće simulaciju komunikacije
 * */
public class Runner {

	public static Izvoriste izvor;
	public static String poruka;
	public static LZ77 lz77;
	public static LZ77Binarno lz77bin;
	public static ArrayList<UredjenaTrojka> prveTrojke;
	public static String binarneTrojke;
	public static HammingCode743 hamming;
	public static String izlazIzHamminga;
	public static Kanal kanal;
	public static String nakonKanala;
	public static String dekodiranoHammingom;
	public static ArrayList<UredjenaTrojka> trojke;
	public static String dekodirano;
	public static String porukaOGresci;
	public static boolean pogreske;
	public static int[] poslanoSimbola;
	public static int[] primljenoSimbola = { 0, 0, 0, 0, 0, 0 };
	public static int[] poslanoBitovaPrijeKod = { 0, 0 };
	public static int[] poslanoBitovaNakonKod = { 0, 0 };
	public static int[] primljenoBitovaPrijeDekod = { 0, 0 };
	public static int[] primljenoBitovaNakonDekod = { 0, 0 };

	public static int posaljiPoruku(int brojSimbola) {

		// generiranje poruke
		izvor = new Izvoriste();
		poruka = izvor.generirajPoruku(brojSimbola);
		poslanoSimbola = izvor.getBrojPojavljivanja();

		// kodiranje u trojke
		lz77 = new LZ77(6, 5);
		lz77bin = new LZ77Binarno();
		prveTrojke = lz77.kodirajNiz(poruka);

		// pretvaranje u binarni zapis
		binarneTrojke = lz77bin.pretvoriTrojkeUBinarno(prveTrojke);
		poslanoBitovaPrijeKod = prebrojiBitove(binarneTrojke);

		// kodiranje hammingom
		hamming = new HammingCode743();
		izlazIzHamminga = hamming.kodirajHammingom(binarneTrojke);
		poslanoBitovaNakonKod = prebrojiBitove(izlazIzHamminga);

		// slanje kroz kanal (sa ili bez pogrešaka)
		kanal = new Kanal(pogreske);
		nakonKanala = kanal.propustiKrozKanal(izlazIzHamminga);
		primljenoBitovaPrijeDekod = prebrojiBitove(nakonKanala);

		// dekodiranje hammingom nakon kanala
		dekodiranoHammingom = hamming.dekodirajHammingom(nakonKanala);
		primljenoBitovaNakonDekod = prebrojiBitove(dekodiranoHammingom);
		trojke = lz77bin.pretvoriNizUTrojke(dekodiranoHammingom);

		// dekodiranje LZ77
		try {
			dekodirano = lz77.dekodirajNiz(trojke);
		} catch (IllegalArgumentException e) {
			dekodirano = lz77.getDekodiraniNiz();
			porukaOGresci = e.getMessage();
			return 2;
		}

		if (dekodirano.equals(poruka)) {
			return 1;
		}
		return 0;
	}

	/**
	 * Računa broj bitova ulaznog niza
	 */
	private static int[] prebrojiBitove(String niz) {

		int[] temp = { 0, 0 };
		for (char c : niz.toCharArray()) {
			if (c == '0')
				temp[0]++;
			else
				temp[1]++;
		}
		return temp;
	}

	/**
	 * Ispis u datoteku i komandnu liniju.
	 */
	public static void ispisi() {

		System.out.println("========");
		System.out.println("PREGLED");
		IspisUDatoteku.upisiUDatoteku("========");
		IspisUDatoteku.upisiUDatoteku("PREGLED");

		System.out
				.println("===================================================================================================================================");
		System.out.println("Poslana poruka:\t\t" + poruka);
		System.out
				.println("===================================================================================================================================");
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");
		IspisUDatoteku.upisiUDatoteku("Poslana poruka:\t\t\t" + poruka);
		IspisUDatoteku.upisiUDatoteku("Poslano simbola:\t\ta="
				+ poslanoSimbola[0] + "  b=" + poslanoSimbola[1] + "  c="
				+ poslanoSimbola[2] + "  d=" + poslanoSimbola[3] + "  e="
				+ poslanoSimbola[4]);
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");

		System.out
				.println("===================================================================================================================================");
		System.out.println("LZ77 koder:\t\t" + prveTrojke);
		System.out
				.println("===================================================================================================================================");
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");
		IspisUDatoteku.upisiUDatoteku("LZ77 koder:\t\t\t" + prveTrojke);
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");

		System.out
				.println("===================================================================================================================================");
		System.out.println("Binarne trojke:\t\t" + binarneTrojke);
		System.out
				.println("===================================================================================================================================");
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");
		IspisUDatoteku.upisiUDatoteku("Binarne trojke:\t\t\t" + binarneTrojke);
		IspisUDatoteku.upisiUDatoteku("Broj bitova:\t\t\t0="
				+ poslanoBitovaPrijeKod[0] + "  1=" + poslanoBitovaPrijeKod[1]);
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");

		System.out
				.println("===================================================================================================================================");
		System.out.println("Kodirano Hammingom:\t" + izlazIzHamminga);
		System.out
				.println("===================================================================================================================================");
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");
		IspisUDatoteku.upisiUDatoteku("Kodirano Hammingom:\t\t"
				+ izlazIzHamminga);
		IspisUDatoteku.upisiUDatoteku("Broj bitova:\t\t\t0="
				+ poslanoBitovaNakonKod[0] + "  1=" + poslanoBitovaNakonKod[1]);
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");

		System.out
				.println("===================================================================================================================================");
		System.out.println("Nakon kanala:\t\t" + nakonKanala);
		System.out
				.println("===================================================================================================================================");
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");
		IspisUDatoteku.upisiUDatoteku("Nakon kanala:\t\t\t" + nakonKanala);
		IspisUDatoteku.upisiUDatoteku("Broj bitova:\t\t\t0="
				+ primljenoBitovaPrijeDekod[0] + "  1="
				+ primljenoBitovaPrijeDekod[1]);
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");

		System.out
				.println("===================================================================================================================================");
		System.out.println("Dekodirano Hammingom:\t\t" + dekodiranoHammingom);
		System.out
				.println("===================================================================================================================================");
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");
		IspisUDatoteku.upisiUDatoteku("Dekodirano Hammingom:\t\t"
				+ dekodiranoHammingom);
		IspisUDatoteku.upisiUDatoteku("Broj bitova:\t\t\t0="
				+ primljenoBitovaNakonDekod[0] + "  1="
				+ primljenoBitovaNakonDekod[1]);
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");

		System.out
				.println("===================================================================================================================================");
		System.out.println("Trojke za LZ77:\t\t" + trojke);
		System.out
				.println("===================================================================================================================================");
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");
		IspisUDatoteku.upisiUDatoteku("Trojke za LZ77:\t\t\t" + trojke);
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");

		if (dekodirano.equals("")) {
			dekodirano = "--";
		}

		System.out
				.println("===================================================================================================================================");
		System.out.println("Primljena poruka:\t" + dekodirano);
		System.out
				.println("===================================================================================================================================");

		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");
		IspisUDatoteku.upisiUDatoteku("Primljena poruka:\t\t" + dekodirano);
		IspisUDatoteku.upisiUDatoteku("Primljeno simbola:\t\ta="
				+ primljenoSimbola[0] + "  b=" + primljenoSimbola[1] + "  c="
				+ primljenoSimbola[2] + "  d=" + primljenoSimbola[3] + "  e="
				+ primljenoSimbola[4] + "  x=" + primljenoSimbola[5]);
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");

		String status = poruka.equals(dekodirano) ? "Uspjesan prijenos"
				: "Neuspjesan prijenos";

		if (porukaOGresci != null) {
			status += " [LZ77 dekodiranje - " + porukaOGresci + "]";
		} else if (!poruka.equals(dekodirano)) {
			status += " [Primljena poruka nije jednaka poslanoj!]";
		}

		System.out.println("Status:\t" + status);
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================");
		IspisUDatoteku.upisiUDatoteku("Status:\t" + status);
		IspisUDatoteku
				.upisiUDatoteku("========================================================================================================================\n\n");

	}

	public static void main(String[] args) {

		int brojSimbola = 0;
		BufferedReader reader = new BufferedReader(new InputStreamReader(
				System.in));
		String unos;

		System.out.print("Koliko simbola zelite generirati: ");
		try {
			unos = reader.readLine();
			brojSimbola = Integer.parseInt(unos);
		} catch (Exception e) {
			System.out.println("Neispravan unos! Prekid programa.");
			System.exit(-2);
		}

		System.out.print("Zelite li kanal s pogreskama? (Y/N): ");
		try {
			unos = reader.readLine();
			if (unos.equalsIgnoreCase("Y")) {
				pogreske = true;
			} else if (unos.equalsIgnoreCase("N")) {
				pogreske = false;
			} else {
				throw new Exception();
			}
		} catch (Exception e) {
			System.out.println("Neispravan unos! Prekid programa.");
			System.exit(-3);
		}

		System.out.println("\nIzvodjenje...\n");

		int status = posaljiPoruku(brojSimbola);
		if (status == 2) {
			System.out
					.println("Dogodila se pogreska prilikom dekodiranja (LZ77): "
							+ porukaOGresci + "\n");
		}
		prebroji(dekodirano);
		ispisi();

		IspisUDatoteku.zatvoriPisac();
	}

	/**
	 * Računa broj pojavljivanja simbola na strani prijemnika.
	 */
	private static void prebroji(String niz) {

		for (char c : niz.toCharArray())
			switch (c) {
			case 'a':
				primljenoSimbola[0]++;
				break;
			case 'b':
				primljenoSimbola[1]++;
				break;
			case 'c':
				primljenoSimbola[2]++;
				break;
			case 'd':
				primljenoSimbola[3]++;
				break;
			case 'e':
				primljenoSimbola[4]++;
				break;
			case 'x':
				primljenoSimbola[5]++;
			}
	}
}
